package framework.util.queryFilter.service.impl;

import framework.util._BeanUtil;
import framework.util.http._HttpRequestUtil;
import framework.util.jdkExt._StringUtil;
import framework.util.layui._LayuiUtil;
import framework.util.queryFilter.vo.OrderBy;
import framework.util.queryFilter.vo.PageBean;
import framework.util.queryFilter.vo.Where;
import framework.util.queryFilter.vo._Field;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 吴宇亮 on 2019/2/15 0015 15:54
 * @blog https://www.iflym.com/index.php/code/201312250002.html
 */
public class QueryFilter {

    public static final Map<Class, String> fieldTypeMap = new HashMap<>(2);
    static {
        //javabean的类型和mybatis的类型 之间的 映射关系
        fieldTypeMap.put(String.class, JdbcType.VARCHAR.name());
        fieldTypeMap.put(Integer.class, JdbcType.INTEGER.name());
        fieldTypeMap.put(Double.class, JdbcType.DOUBLE.name());
        fieldTypeMap.put(Long.class, JdbcType.DECIMAL.name());
    }

    /** 查询的table名称 **/
    private String tableName;
    /** 要查询bo的字段列表 **/
    private List<_Field> fieldList;
    /** pageVo, 包含分页等信息 **/
    private PageBean pageBean;
    /** 是否为distinct select, 默认为false **/
    private boolean isDistinctSelect = false;
    /** 要select的字段, 默认为'*'查询全部字段 **/
    private List<String> selectList = new ArrayList<>();
    /** 要过滤的条件集合 **/
    private List<Where> whereList = new ArrayList<>();
    /** 要进行排序的排序集合 **/
    private List<OrderBy> orderByList = new ArrayList<>();
    /** 查询参数map，用于给映射文件通过'#'号注入查询值。<br/>
     *  在IBaseServiceImpl生成完sql，如：lid = #{_q.lid, jdbcType=VARCHAR} <br/>
     *  然后将这个paramMap传递给Mybatis。Mybatis就会从这个paramMap拿到lid真正的值，相当于paramMap.get("lid")
     **/
    private Map<String, Object> paramMap = new HashMap<>();


    /**
     * 自动解析请求参数，手工开启是否分页
     */
    public QueryFilter(HttpServletRequest request, Boolean isStartPaging){
        ///第一步：处理分页
        int page = 0, rows = 0;
        //如果开启了分页，则按请求参数的来
        if(isStartPaging){
            String _page = request.getParameter(_LayuiUtil.PAGE)
                    ,_rows = request.getParameter(_LayuiUtil.ROWS);
            page = StringUtils.isNoneBlank(_page) ? Integer.valueOf(_page) : PageBean.DEFAULT_START_PAGE;
            rows = StringUtils.isNoneBlank(_rows) ? Integer.valueOf(_rows) : PageBean.DEFAULT_PAGE_ROWS;
        //如果没有开启分页，则查全部
        }else{
            page = _LayuiUtil.FIRST_PAGE;
            rows = Integer.MAX_VALUE;
        }
        pageBean = new PageBean(page, rows);

        ///第二步：处理排序
        String orderFields = request.getParameter(_LayuiUtil.ORDER_FIELDS);
        String orderRules = request.getParameter(_LayuiUtil.ORDER_RULES);
        if(StringUtils.isNoneBlank(orderFields, orderRules)){
            List<String> orderFieldList = _StringUtil.split(orderFields, ",", "，");
            List<String> orderRuleList = _StringUtil.split(orderRules, ",", "，");

            orderFieldList = orderFieldList.stream().map(_BeanUtil::transformUpperCase2_).collect(Collectors.toList());
            this.orderBy(orderFieldList, orderRuleList);
        }

        ///第三步：处理where，自动过滤
        this.autoFilterByRequest(request);
    }

    /**
     * 手动分页
     * @param page 查询第page页
     * @param rows 一页展示rows条数据
     */
    public QueryFilter(int page, int rows){
        if(page <= 0){
            throw new IllegalArgumentException("分页：开始页数不能小于1");
        }
        if(rows <= 0){
            throw new IllegalArgumentException("分页：一页展示的条数要大于0");
        }
        pageBean = new PageBean(page, rows);
    }

    /**
     * 自动根据request的条件进行过滤。
     *
     */
    private void autoFilterByRequest(HttpServletRequest request) {
        Map<String, String> requestMap = _HttpRequestUtil.getRequestMap(request);
        for (String key : requestMap.keySet()) {
            if(!key.startsWith("where")){
                continue;
            }
            String value = requestMap.get(key);
            if(StringUtils.isBlank(value)){
                continue;
            }
            String[] arr = key.split("_");
            if(arr.length < 3){
                continue; //不符合自动过滤的规则
            }
            String fieldName = arr[1], filterType = arr[2];
            this.where(fieldName, filterType, value);
        }
    }

    /**
     * 为QueryFilter添加查询条件
     */
    public void where(String whereField, String whereType, Object whereValue){
        whereList.add(new Where(whereField, whereType, whereValue, paramMap));
    }

    /**
     * 对fieldName字段进行排序。当一个QueryFilter对象被多次调用了orderBy方法，会进行叠加。比如：<br/>
     * <code>
     *     filter.orderBy("age", "asc"); <br/>
     *     filter.orderBy("money", "desc"); <br/>
     * </code>
     * 最后生成的sql为：order by age asc, money desc
     * @param fieldName 待排序的字段
     * @param orderby desc或asc
     */
    public void orderBy(String fieldName, String orderby){
        orderByList.add(new OrderBy(fieldName, orderby));
    }

    /**
     * 对fieldName字段进行排序。当一个QueryFilter对象被多次调用了orderBy方法，会进行叠加。比如：<br/>
     * <code>
     *     filter.orderBy(new String[]{"age", "money"}, new String[]{"asc", "desc"}); <br/>
     * </code>
     * 最后生成的sql为：order by age asc, money desc
     * @param fieldNameArr 待排序的字段数组
     * @param orderbyArr 排序规则数组, desc或asc
     */
    public void orderBy(String[] fieldNameArr, String[] orderbyArr){
        if(null == fieldNameArr || fieldNameArr.length == 0 || null == orderbyArr || orderbyArr.length == 0){
            throw new IllegalArgumentException("待排序的字段 或者 排序规则 不能为空");
        }else if(fieldNameArr.length != orderbyArr.length){
            throw new IllegalArgumentException("待排序的字段 和 排序规则 的数量不一致");
        }

        for(int i = 0; i < fieldNameArr.length; i++){
            this.orderBy(fieldNameArr[i], orderbyArr[i]);
        }
    }

    /**
     * 对fieldName字段进行排序。当一个QueryFilter对象被多次调用了orderBy方法，会进行叠加。比如：<br/>
     * <code>
     *     filter.orderBy(Lists.newArrayList("age", "money"), Lists.newArrayList("asc", "desc")); <br/>
     * </code>
     * 最后生成的sql为：order by age asc, money desc
     * @param fieldNameList 待排序的字段List
     * @param orderbyList 排序规则List, desc或asc
     */
    public void orderBy(List<String> fieldNameList, List<String> orderbyList){
        if(CollectionUtils.isEmpty(fieldNameList) || CollectionUtils.isEmpty(orderbyList)){
            throw new IllegalArgumentException("待排序的字段 或者 排序规则 不能为空");
        }else if(fieldNameList.size() != orderbyList.size()){
            throw new IllegalArgumentException("待排序的字段 和 排序规则 的数量不一致");
        }

        for(int i = 0; i < fieldNameList.size(); i++){
            this.orderBy(fieldNameList.get(i), orderbyList.get(i));
        }
    }

    /**
     * 自动根据request的过滤条件生成sql，并且放入到whereSql中。
     */
    public static void autoFilterByRequestMap(Map<String, String> requestMap) {
        //请求方法名不需要
        requestMap.remove(_LayuiUtil.METHOD);

        StringBuilder sql = new StringBuilder();
        String page = null, rows = null;
        String orderFields = null, orderRules = null;
        for (String key : requestMap.keySet()) {
            if(_LayuiUtil.PAGE.equals(key)){
                page = requestMap.get(key);
                continue;
            }else if(_LayuiUtil.ROWS.equals(key)){
                rows = requestMap.get(key);
                continue;
            }else if(_LayuiUtil.ORDER_FIELDS.equals(key)){
                orderFields = requestMap.get(key);
                continue;
            }else if(_LayuiUtil.ORDER_RULES.equals(key)){
                orderRules = requestMap.get(key);
                continue;
            }else if(!key.startsWith("where")){
                continue;
            }
            String[] arr = key.split("_");
            if(arr.length < 3){
                continue; //不符合自动过滤的规则
            }
            String fieldName = arr[1], filterType = arr[2];

            String value = requestMap.get(key);
            if(StringUtils.isBlank(value)
                    && !filterType.equalsIgnoreCase(Where.IsNotNull)
                    && !filterType.equalsIgnoreCase(Where.isNull)){
                continue;
            }
            sql.append(" and " + Where.generatorSql(fieldName, filterType, value));
        }
        //如果有自动过滤
        if(sql.length() > 0){
            requestMap.put("whereSql", sql.toString());
        }
        //如果有开启分页
        if(StringUtils.isNoneBlank(page, rows)){
            PageBean pageBean = new PageBean(Integer.parseInt(page), Integer.parseInt(rows));
            String limitSql = "limit " + pageBean.getOfferset() + ", " + rows;
            requestMap.put("limitSql", limitSql);
        }
        //如果有设置排序
        if(StringUtils.isNoneBlank(orderFields, orderRules)){
            List<String> orderFieldList = _StringUtil.split(orderFields, ",", "，");
            List<String> orderRuleList = _StringUtil.split(orderRules, ",", "，");
            if(orderFieldList.size() != orderRuleList.size()){
                throw new IllegalArgumentException("排序参数 与 排序规则 数量不一致");
            }

            StringBuilder orderBySql = new StringBuilder(orderFieldList.size() * 20);
            orderFieldList = orderFieldList.stream().map(_BeanUtil::transformUpperCase2_).collect(Collectors.toList());
            for (int i = 0; i < orderFieldList.size(); i++) {
                if(i == 0){
                    orderBySql.append("order by " + orderFieldList.get(i) + " " + orderRuleList.get(i));
                }else{
                    orderBySql.append(", " + orderFieldList.get(i) + " " + orderRuleList.get(i));
                }
            }
            requestMap.put("orderBySql", orderBySql.toString());
        }
    }

    /**
     * 获取分页对象
     */
    public PageBean getPageBean() {
        return pageBean;
    }

    /**
     * 设置是否开启distinct查询，默认为false
     */
    public void setDistinctSelect(boolean distinctSelect) {
        isDistinctSelect = distinctSelect;
    }

    /**
     * 设置要查询的字段，默认查询全部字段，<B>用于提高查询效率</B>。
     */
    public void setSelectList(List<String> fieldName) {
        this.selectList = fieldName;
    }

    //因为如果访问权限不为public，MyBatis就调用不了了
    /**
     * 获取即将要查询的bo的表名。CURD底层 <br/>
     * <b>不建议使用原因：CURD底层，一般操作是获取不了值的</b>
     */
    @Deprecated
    public String getTableName() {
        return tableName;
    }

    //因为如果访问权限不为public，MyBatis就调用不了了
    /**
     * 获取要select的字段名。CURD底层<br/>
     * <b>不建议使用原因：CURD底层，一般操作是获取不了值的</b>
     */
    @Deprecated
    public List<String> getSelectList() {
        return selectList;
    }

    //因为如果访问权限不为public，MyBatis就调用不了了
    /**
     * 获取是否要开启distinct查询。CURD底层<br/>
     * <b>不建议使用原因：CURD底层，一般操作是获取不了值的</b>
     */
    @Deprecated
    public boolean isDistinctSelect() {
        return isDistinctSelect;
    }

    public void clearWhereList(){
        whereList.clear();
    }

    @Deprecated
    public List<_Field> getFieldList() {
        return fieldList;
    }

    @Deprecated
    public List<Where> getWhereList() {
        return whereList;
    }

    @Deprecated
    public List<OrderBy> getOrderByList() {
        return orderByList;
    }

    public Map<String, Object> getParamMap() {
        return paramMap;
    }

    protected void setTableName(String tableName) {
        this.tableName = tableName;
    }

    protected void setFieldList(List<_Field> fieldList) {
        this.fieldList = fieldList;
    }

    protected void setPageBean(PageBean pageBean) {
        this.pageBean = pageBean;
    }

    protected void setWhereList(List<Where> whereList) {
        this.whereList = whereList;
    }

    protected void setOrderByList(List<OrderBy> orderByList) {
        this.orderByList = orderByList;
    }
}
